package com.kujie.common.modbus;

import com.digitalpetri.modbus.codec.Modbus;
import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.ReadCoilsRequest;
import com.digitalpetri.modbus.requests.ReadDiscreteInputsRequest;
import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;
import com.digitalpetri.modbus.requests.ReadInputRegistersRequest;
import com.digitalpetri.modbus.responses.ReadCoilsResponse;
import com.digitalpetri.modbus.responses.ReadDiscreteInputsResponse;
import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;
import com.digitalpetri.modbus.responses.ReadInputRegistersResponse;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * modbus TCP协议Java通讯读取例子
 *
 * @author jyy
 * @date 2025-02-07
 */
@Slf4j
public class ModbusMasterTcpUtil {

    static ModbusTcpMaster master;

    /**
     * 获取TCP协议的Master
     *
     * @return ModbusTcpMaster
     */
    public static ModbusTcpMaster getMaster() {
        if (master == null) {
            // 创建配置
            ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("localhost").setPort(502).build();
            master = new ModbusTcpMaster(config);
        }
        return master;
    }

    /***
     * 释放资源
     */
    public static void release() {
        if (master != null) {
            master.disconnect();
        }
        Modbus.releaseSharedResources();
    }

    /**
     * 读取Coils开关量
     *
     * @param address  寄存器开始地址
     * @param quantity 寄存器数量
     * @param unitId   ID
     * @return 读取值
     * @throws InterruptedException 异常
     * @throws ExecutionException   异常
     */
    public static Boolean readCoils(int address, int quantity, int unitId)
        throws InterruptedException, ExecutionException {
        Boolean result = null;
        CompletableFuture<ReadCoilsResponse> future = getMaster().sendRequest(new ReadCoilsRequest(address, quantity),
            unitId);
        // 同步返回.实际使用推荐结合业务进行异步处理
        ReadCoilsResponse readCoilsResponse = future.get();
        if (readCoilsResponse != null) {
            ByteBuf buf = readCoilsResponse.getCoilStatus();
            result = buf.readBoolean();
            ReferenceCountUtil.release(readCoilsResponse);
        }
        return result;
    }

    /**
     * 读取readDiscreteInputs开关量
     *
     * @param address  寄存器开始地址
     * @param quantity 寄存器数量
     * @param unitId   ID
     * @return 读取值
     * @throws InterruptedException 异常
     * @throws ExecutionException   异常
     */
    public static Boolean readDiscreteInputs(int address, int quantity, int unitId)
        throws InterruptedException, ExecutionException {
        Boolean result = null;
        CompletableFuture<ReadDiscreteInputsResponse> future = getMaster()
            .sendRequest(new ReadDiscreteInputsRequest(address, quantity), unitId);
        //同步返回数据
        ReadDiscreteInputsResponse discreteInputsResponse = future.get();
        if (discreteInputsResponse != null) {
            ByteBuf buf = discreteInputsResponse.getInputStatus();
            result = buf.readBoolean();
            ReferenceCountUtil.release(discreteInputsResponse);
        }
        return result;
    }

    /**
     * 读取HoldingRegister数据
     *
     * @param address  寄存器地址
     * @param quantity 寄存器数量
     * @param unitId   id
     * @return 读取结果
     * @throws InterruptedException 异常
     * @throws ExecutionException   异常
     */
    public static Number readHoldingRegisters(int address, int quantity, int unitId)
        throws InterruptedException, ExecutionException {
        Number result = null;
        CompletableFuture<ReadHoldingRegistersResponse> future = getMaster()
            .sendRequest(new ReadHoldingRegistersRequest(address, quantity), unitId);
        //同步返回数据
        ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get();
        if (readHoldingRegistersResponse != null) {
            ByteBuf buf = readHoldingRegistersResponse.getRegisters();
            result = buf.readFloat();
            ReferenceCountUtil.release(readHoldingRegistersResponse);
        }
        return result;
    }

    /**
     * 读取InputRegisters模拟量数据
     *
     * @param address  寄存器开始地址
     * @param quantity 寄存器数量
     * @param unitId   ID
     * @return 读取值
     * @throws InterruptedException 异常
     * @throws ExecutionException   异常
     */
    public static Number readInputRegisters(int address, int quantity, int unitId)
        throws InterruptedException, ExecutionException {
        Number result = null;
        CompletableFuture<ReadInputRegistersResponse> future = getMaster()
            .sendRequest(new ReadInputRegistersRequest(address, quantity), unitId);
        //同步返回数据
        ReadInputRegistersResponse readInputRegistersResponse = future.get();
        if (readInputRegistersResponse != null) {
            ByteBuf buf = readInputRegistersResponse.getRegisters();
            result = buf.readDouble();
            ReferenceCountUtil.release(readInputRegistersResponse);
        }
        return result;
    }

    /**
     * 异步取值
     *
     * @param address  寄存器开始地址
     * @param quantity 寄存器数量
     * @param unitId   ID
     */
    public static void asyncReadHoldingRegisters(int address, int quantity, int unitId) {
        try {
            CompletableFuture<ReadHoldingRegistersResponse> future =
                getMaster().sendRequest(new ReadHoldingRegistersRequest(address, quantity), unitId);

            System.out.println("这个是异步方法---开始");
            future.thenAccept(response -> {
                System.out.println("这里是异步取值: " + response.getRegisters().readFloat());
                ReferenceCountUtil.release(response);
                //不能在异步方法外面直接断开连接，不然异步方法娶不到值。在什么情况下断开连接，大家根据自己实际业务
                release();
            });
            System.out.println("这个是异步方法---结束");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            System.out.println("finally end");
        }
    }

    public static void main(String[] args) {
        try {
            // 初始化资源
            getMaster();

            // 执行操作

            // 读取开关量
            System.out.println(readCoils(3, 1, 1));
            System.out.println(readDiscreteInputs(0, 1, 1));
            System.out.println(readDiscreteInputs(1, 1, 1));

            // 读取模拟量
            System.out.println(readHoldingRegisters(0, 2, 1));
            System.out.println(readHoldingRegisters(2, 2, 1));
            System.out.println(readHoldingRegisters(4, 2, 1));
            System.out.println(readInputRegisters(2, 4, 1));
            System.out.println(readInputRegisters(6, 4, 1));

            asyncReadHoldingRegisters(2, 2, 1);

            // 释放资源
//            release();
        } catch (Exception e) {
            log.error(e.toString());
        }
    }
}
